package com.xbongbong.paas.elasticsearch.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.i18n.util.I18nMessageUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.SizeConstant;
import com.xbongbong.paas.domain.entity.PaasFormDataEntity;
import com.xbongbong.paas.domain.entity.ext.PaasFormDataEntityExt;
import com.xbongbong.paas.elasticsearch.pojo.AggMorePojo;
import com.xbongbong.paas.elasticsearch.pojo.AggSubFactoriesPojo;
import com.xbongbong.paas.elasticsearch.pojo.ElasticFindPojo;
import com.xbongbong.paas.elasticsearch.pojo.XbbAggregatedPage;
import com.xbongbong.paas.elasticsearch.pojo.dto.TopHitsDTO;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.enums.IndexTypeEnum;
import com.xbongbong.paas.pojo.dto.AggDTO;
import com.xbongbong.paas.pojo.dto.AggSumDTO;
import com.xbongbong.paas.pojo.dto.PaasFormDataEsMappingDTO;
import com.xbongbong.paas.pojo.vo.AggSumVO;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.helper.PageHelper;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.BeanUtil;
import com.xbongbong.paas.toolbox.util.DateUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.pro.constant.ElasticConstant;
import com.xbongbong.pro.constant.I18nStringConstant;
import com.xbongbong.pro.enums.IntervalTypeEnum;
import com.xbongbong.pro.enums.TimeTypeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.statistic.constant.ChartConstant;
import com.xbongbong.pro.statistic.result.pojo.AggsBucketsPojo;
import com.xbongbong.pro.statistic.result.pojo.ElasticAggsPojo;
import com.xbongbong.saas.enums.UserTeamEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.ClueEnum;
import com.xbongbong.saas.enums.business.CommunicatePlanEnum;
import com.xbongbong.saas.enums.business.ContactEnum;
import com.xbongbong.saas.enums.business.ContractOutstockEnum;
import com.xbongbong.saas.enums.business.CustomerManagementEnum;
import com.xbongbong.saas.enums.business.InvoiceEnum;
import com.xbongbong.saas.enums.business.PaymentTaskEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.business.PurchaseInvoiceEnum;
import com.xbongbong.saas.enums.business.SupplierContactEnum;
import com.xbongbong.saas.enums.business.SupplierEnum;
import com.xbongbong.saas.enums.business.WorkOrderEnum;
import com.xbongbong.saas.enums.business.WorkReportDailyEnum;
import com.xbongbong.saas.enums.subform.ProductWarehouseSubFormEnum;
import com.xbongbong.saas.enums.subform.SubFormTransferEnum;
import com.xbongbong.saas.toolbox.help.BasicHelper;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.AggregatorFactories;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.SumAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.TopHitsAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.IndexQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * es查询（统计）相关方法
 * 创建时间： 2018/9/28 11:00
 * 修改时间： 2018/9/28 11:00 梁鲁江
 * index type indexTypeEnum 核对完成
 * @author 梁鲁江
 */
@Component
public class EsHelper {
    private static final Logger LOG = LoggerFactory.getLogger(EsHelper.class);
    @Resource
    private XbbElasticsearchRestTemplate xbbElasticsearchRestTemplate;
    /**
     * 初始化
     * @param paasFormDataEntity
     * @return
     */
    public static IndexQuery getIndexQuery(PaasFormDataEntity paasFormDataEntity) {
        paasFormDataEntity.setEsId(paasFormDataEntity.getCorpid() + "_" + paasFormDataEntity.getId());
        return new IndexQueryBuilder()
                .withId(paasFormDataEntity.getEsId())
                //TODO 区分不同索引
//                .withIndexName()
//                .withType()
                .withObject(paasFormDataEntity)
                .build();
    }

    /**
     * 批量初始化
     * @param paasFormDataEntityList
     * @return
     */
    public static List<IndexQuery> getIndexQueries(List<PaasFormDataEntity> paasFormDataEntityList) {
        List<IndexQuery> indexQueries = new ArrayList<>();
        for (PaasFormDataEntity entity : paasFormDataEntityList) {
            indexQueries.add(getIndexQuery(entity));
        }
        return indexQueries;
    }

    /**
     * 格式化分组查询结果（根据ES返回的的解决转化给前端标准输出）
     * @return
     * @throws IOException
     */
    public String agg(AggDTO aggDTO) throws IOException {
        String corpid = aggDTO.getCorpid();
        Integer page = aggDTO.getPage();
        Integer pageSize = aggDTO.getPageSize();
        String displayAttr = aggDTO.getDisplayAttr();
        Integer type = aggDTO.getType();
        List<String> groupList = aggDTO.getGroupList();
        //SAAS展示字段（别名）和分组字段会有后缀的不同，PASS若没有可以归一0
        List<String> showList = new ArrayList<>();
        showList.addAll(groupList);
        Integer isExport = aggDTO.getIsExport();
        Map<String, String> summaryAttrMap = aggDTO.getSummaryAttrMap();
        String redisKey = aggDTO.getRedisKey();
        JSONObject esResponseJson = aggDTO.getEsResponseJson();
        List<String> sumList = new ArrayList<>();
        for (Map.Entry<String, String> entry : summaryAttrMap.entrySet()) {
            sumList.add(EsUtil.getHumpString(entry.getKey()));
        }


        JSONObject newJsonObject = new JSONObject(true);
        List<Object> outList = new ArrayList<>();
        JSONObject groupCountJson = new JSONObject(true);

        //TODO  后期缓存的使用
        String groupRedisKey = "_group_" + redisKey;
        Map<String,Object> redisGroupMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        redisGroupMap = null;
        if (redisGroupMap != null) {
            List<Object> redisOutList = (List<Object>) redisGroupMap.get("outList");
            JSONObject redisNewJsonObject = (JSONObject) redisGroupMap.get("newJsonObject");
            JSONObject redisGroupCountJson = (JSONObject) redisGroupMap.get("groupCountJson");
            if (redisOutList != null && !redisOutList.isEmpty()) {
                outList.addAll(redisOutList);
            }
            if(redisNewJsonObject != null && !redisNewJsonObject.isEmpty()) {
                newJsonObject.putAll(redisNewJsonObject);
            }
            if(redisGroupCountJson != null && !redisGroupCountJson.isEmpty()) {
                groupCountJson.putAll(redisGroupCountJson);
            }
        }else {
            groupList.addAll(showList);
            // 获取usersMap<userId,userName>
//            Map<String, String> userMap = EsUtil.getUsersMap(corpid,  userModel);
            Integer groupTotal = 0;
            JSONArray group1R = esResponseJson.getJSONObject("aggregations").getJSONObject(showList.get(0)).getJSONArray("buckets");
            if (group1R != null && !group1R.isEmpty()) {
//                ExplainDataDTO explainDataDTO = new ExplainDataDTO();
//                BeanUtils.copyProperties(aggDTO, explainDataDTO);
//                explainDataDTO.setUserMap(userMap);
                for (int i = 0; i < group1R.size(); i++) {
                    Integer groupCount = 0;
                    JSONObject object1 = group1R.getJSONObject(i);
                    JSONObject dataObject = new JSONObject();
                    dataObject.put(showList.get(0), object1.get("key"));
//                    explainDataDTO.setDataObject(dataObject);
//                    explainData(explainDataDTO);
                    String group1 = EsUtil.createGroupAttrValue(0, groupList, dataObject);
                    JSONArray filterCriteriaArry = new JSONArray();
                    JSONObject keyObject0 = new JSONObject();
                    JSONObject group1JsonObject = new JSONObject();
                    group1JsonObject.put("value", group1);
                    String group1Copy = (I18nMessageUtil.getMessage(I18nStringConstant.UNKNOW).equals(group1)) ? "-1" : group1;
                    String key0 = (StringUtil
                            .isEmpty(dataObject.getString(EsUtil.getHumpString(groupList.get(0) + "Copy"))))
                            ? group1Copy
                            : (dataObject.getString(EsUtil.getHumpString(groupList.get(0) + "Copy")));
                    keyObject0.put("value", key0);
                    keyObject0.put("valueName", group1);
                    keyObject0.put("attr", groupList.get(0));
                    filterCriteriaArry.add(keyObject0);
                    group1JsonObject.put("key", filterCriteriaArry);
                    //大于两层
                    if (groupList.size() > 1) {
                        JSONArray group2R = object1.getJSONObject(showList.get(1)).getJSONArray("buckets");
                        JSONObject sumMap = new JSONObject();
                        List<Object> innerList = new ArrayList<>();
                        if (group2R != null && !group2R.isEmpty()) {
                            for (int i1 = 0; i1 < group2R.size(); i1++) {
                                JSONObject object2 = group2R.getJSONObject(i1);
                                dataObject = new JSONObject();
                                dataObject.put(showList.get(1), object2.get("key"));
                                //
//                                explainDataDTO.setDataObject(dataObject);
//                                explainData(explainDataDTO);
                                String group2 = EsUtil.createGroupAttrValue(1, groupList, dataObject);
                                if (StringUtil.isEmpty(group2)) {
                                    group2 = I18nMessageUtil.getMessage(I18nStringConstant.UNKNOW);
                                }
                                JSONObject keyObject1 = new JSONObject();
                                JSONObject group2JsonObject = new JSONObject();
                                group2JsonObject.put("value", group2);
                                String group2Copy = (I18nMessageUtil.getMessage(I18nStringConstant.UNKNOW).equals(group2)) ? "-1" : group2;
                                String key1 = (StringUtil.isEmpty(dataObject.getString(EsUtil.getHumpString(groupList.get(1) + "Copy"))))? group2Copy: (dataObject.getString(EsUtil.getHumpString(groupList.get(1) + "Copy")));
                                keyObject1.put("value", key1);
                                keyObject1.put("valueName", group2);
                                keyObject1.put("attr", groupList.get(1));
                                //
                                JSONArray filterCriteriaArry1 = new JSONArray();
                                filterCriteriaArry = group1JsonObject.getJSONArray("key");
                                filterCriteriaArry1.addAll(filterCriteriaArry);
                                filterCriteriaArry1.add(keyObject1);
                                group2JsonObject.put("key", filterCriteriaArry1);
                                //有三层
                                if(groupList.size() > 2) {
                                    JSONArray group3R = object2.getJSONObject(showList.get(2)).getJSONArray("buckets");
                                    if (group3R != null && !group3R.isEmpty()) {
                                        for (int i2 = 0; i2 < group3R.size(); i2++) {
                                            JSONObject object3 = group3R.getJSONObject(i2);
                                            dataObject = new JSONObject();
                                            dataObject.put(showList.get(2), object3.get("key"));
//                                            explainDataDTO.setDataObject(dataObject);
//                                            explainData(explainDataDTO);
                                            String group3 = EsUtil.createGroupAttrValue(2, groupList, dataObject);
                                            if (StringUtil.isEmpty(group3)) {
                                                group3 = I18nMessageUtil.getMessage(I18nStringConstant.UNKNOW);
                                            }
                                            JSONObject keyObject3 = new JSONObject();
                                            JSONObject group3JsonObject = new JSONObject();
                                            group3JsonObject.put("value", group3);
                                            String group3Copy = (I18nMessageUtil.getMessage(I18nStringConstant.UNKNOW).equals(group3)) ? "-1" : group3;
                                            String key3 = (StringUtil.isEmpty(dataObject.getString(EsUtil.getHumpString(groupList.get(2) + "Copy"))))? group3Copy: (dataObject.getString(EsUtil.getHumpString(groupList.get(2) + "Copy")));
                                            keyObject3.put("value", key3);
                                            keyObject3.put("valueName", group3);
                                            keyObject3.put("attr", groupList.get(2));
                                            //
                                            JSONArray filterCriteriaArry3 = new JSONArray();
                                            filterCriteriaArry = group2JsonObject.getJSONArray("key");
                                            filterCriteriaArry3.addAll(filterCriteriaArry);
                                            filterCriteriaArry3.add(keyObject3);
                                            group3JsonObject.put("key", filterCriteriaArry3);

                                            List<Object> list = new ArrayList<>();

                                            for (int j = 0; j < sumList.size(); j++) {
                                                Double value = object3.getJSONObject(sumList.get(j)).getDouble("value");
                                                list.add(StringUtil.formatDouble(value));
                                                Double valueObject = sumMap.getDouble(sumList.get(j));
                                                if (valueObject == null) {
                                                    valueObject = 0D;
                                                }
                                                sumMap.put(sumList.get(j), valueObject + value);
                                                groupCount++;
                                            }
                                            Integer docCount = object3.getInteger("doc_count");
                                            list.add(docCount);
                                            Integer valueObject = sumMap.getInteger("doc_count");
                                            if (valueObject == null) {
                                                valueObject = 0;
                                            }
                                            sumMap.put("doc_count", valueObject + docCount);

                                            JSONObject jsonObject1 = newJsonObject.getJSONObject(group1JsonObject.toJSONString());
                                            if (jsonObject1 == null) {
                                                jsonObject1 = new JSONObject();
                                                newJsonObject.put(group1JsonObject.toJSONString(), jsonObject1);
                                            }
                                            JSONObject jsonObject2 = jsonObject1.getJSONObject(group2JsonObject.toJSONString());
                                            if (jsonObject2 == null) {
                                                jsonObject2 = new JSONObject();
                                                jsonObject1.put(group2JsonObject.toJSONString(), jsonObject2);
                                            }
                                            JSONArray jsonObject3 = jsonObject2.getJSONArray(group3JsonObject.toJSONString());
                                            if (jsonObject3 == null) {
                                                jsonObject3 = new JSONArray();
                                                jsonObject2.put(group3JsonObject.toJSONString(), jsonObject3);
                                            }
                                            jsonObject3.addAll(list);
                                        }
                                    }

                                }else {
                                    //只有两个分组字段
                                    List<Object> list = new ArrayList<>();
                                    for (int j = 0; j < sumList.size(); j++) {
                                        Double value = object2.getJSONObject(sumList.get(j)).getDouble("value");
                                        list.add(StringUtil.formatDouble(value));
                                        Double valueObject = sumMap.getDouble(sumList.get(j));
                                        if (valueObject == null) {
                                            valueObject = 0D;
                                        }
                                        sumMap.put(sumList.get(j), valueObject + value);
                                        groupCount++;
                                    }
                                    Integer docCount = object2.getInteger("doc_count");
                                    list.add(docCount);
                                    Integer valueObject = sumMap.getInteger("doc_count");
                                    if (valueObject == null) {
                                        valueObject = 0;
                                    }
                                    sumMap.put("doc_count", valueObject + docCount);

                                    JSONObject jsonObject1 = newJsonObject.getJSONObject(group1JsonObject.toJSONString());
                                    if (jsonObject1 == null) {
                                        jsonObject1 = new JSONObject();
                                        newJsonObject.put(group1JsonObject.toJSONString(), jsonObject1);
                                    }
                                    JSONArray jsonObject2 = jsonObject1.getJSONArray(group2JsonObject.toJSONString());
                                    if (jsonObject2 == null) {
                                        jsonObject2 = new JSONArray();
                                        jsonObject1.put(group2JsonObject.toJSONString(), jsonObject2);
                                    }
                                    jsonObject2.addAll(list);
                                }
                            }
                            if(groupList.size() > 2) {
                                innerList.add("");
                            }
                            innerList.add("");
                            innerList.add("");
                            for (int j = 0; j < sumList.size(); j++) {
                                Double value = sumMap.getDouble(sumList.get(j));
                                if (value == null) {
                                    value = 0D;
                                }
                                innerList.add(StringUtil.formatDouble(value));
                            }
                            innerList.add(sumMap.getInteger("doc_count"));
                            outList.add(innerList);
                        }
                    } else {
                        // 只有一个分组字段
                        JSONArray jsonObject1 = newJsonObject.getJSONArray(group1JsonObject.toJSONString());
                        if (jsonObject1 == null) {
                            jsonObject1 = new JSONArray();
                            newJsonObject.put(group1JsonObject.toJSONString(), jsonObject1);
                        }

                        JSONObject sumMap = new JSONObject();
                        List<Object> list = new ArrayList<>();
                        for (int j = 0; j < sumList.size(); j++) {
                            Double value = object1.getJSONObject(sumList.get(j)).getDouble("value");
                            list.add(StringUtil.formatDouble(value));
                            Double valueObject = sumMap.getDouble(sumList.get(j));
                            if (valueObject == null) {
                                valueObject = 0D;
                            }
                            sumMap.put(sumList.get(j), valueObject + value);
                            groupCount++;
                        }
                        Integer docCount = object1.getInteger("doc_count");
                        list.add(docCount);
                        Integer valueObject = sumMap.getInteger("doc_count");
                        if (valueObject == null) {
                            valueObject = 0;
                        }
                        sumMap.put("doc_count", valueObject + docCount);
                        jsonObject1.addAll(list);
                        List<Object> innerList = new ArrayList<>();
                        innerList.add("");
                        for (int j = 0; j < sumList.size(); j++) {
                            Double valueObject1 = sumMap.getDouble(sumList.get(j));
                            if (valueObject1 == null) {
                                valueObject1 = 0D;
                            }
                            innerList.add(StringUtil.formatDouble(valueObject1));
                        }
                        innerList.add(sumMap.getInteger("doc_count"));
                        outList.add(innerList);
                    }
                    groupCountJson.put(group1JsonObject.toJSONString(), groupCount);
                    groupTotal += groupCount;
                }
            }
   /*         Map<String,Object> redisMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            redisMap.put("outList",outList);
            redisMap.put("newJsonObject",newJsonObject);
            redisMap.put("groupCountJson",groupCountJson);
            JedisUtils.setJedisValue(RedisPrefixConstant.PG_STATISTIC, groupRedisKey, redisMap, RedisTimeConstant.TEN_MINUTES);*/
        }

        if(isExport == 1) {
            JSONObject object = new JSONObject();
            JSONArray list = new JSONArray();
            for (Map.Entry<String, Object> entry : newJsonObject.entrySet()) {
                list.add(entry);
            }
            object.put("groupList", list.toJSONString());
            object.put("subtotalList", outList);
            String statisValue = object.toJSONString();
            return statisValue;
        }

        //分页的处理
        PageHelper pageHelper = new PageHelper(page, pageSize);
        Integer dividPageEnd = 0;
        Integer  lineNumber = 0;
        Integer virtualPage = 0;
        Map<Integer,Integer> pageMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (Map.Entry<String, Object> entry2 : groupCountJson.entrySet()) {
            //每个大分组的行数，排除小计
            Integer sub = Integer.valueOf(entry2.getValue().toString());
            if (sub != null) {
                if(lineNumber == 0) {
                    virtualPage++;
                }
                lineNumber += sub;
                if (lineNumber >  pageSize) {
                    lineNumber = 0;
                }
                dividPageEnd ++;
                pageMap.put(virtualPage, dividPageEnd);
            }
        }
        pageHelper.setRowsCount(pageMap.size() * pageSize);
        pageMap.put(0, 0);

        JSONObject object = new JSONObject();
        if(newJsonObject == null || newJsonObject.isEmpty()) {
            object.put("pageHelper", pageHelper);
            object.put("groupList", new JSONArray());
            object.put("subtotalList", new ArrayList<>());
            return object.toJSONString();
        }
        JSONArray list = new JSONArray();
        for (Map.Entry<String, Object> entry : newJsonObject.entrySet()) {
            list.add(entry);
        }

        object.put("groupList", JSONArray.parseArray(JSON.toJSONString(list.subList(pageMap.get(page-1), pageMap.get(page)))).toJSONString());
        object.put("subtotalList", outList.subList(pageMap.get(page-1), pageMap.get(page)));
        object.put("pageHelper", pageHelper);
        //汇总总计的部分
        if(isExport == 0) {
            JSONArray summaryDisplay = new JSONArray();
            String sumRedisKey = "_sum_" + redisKey;
//            JSONArray  redisSummaryDisplay = (JSONArray) JedisUtils.getJedisValue(RedisPrefixConstant.PG_STATISTIC,sumRedisKey);
            JSONArray  redisSummaryDisplay = null;
            if (redisSummaryDisplay != null) {
                summaryDisplay.addAll(redisSummaryDisplay);
            }else{
//                getSumResult(corpid, elasticQueryDSL, pageHelper, summaryDisplay, summaryAttrMap,indexType);
//                JedisUtils.setJedisValue(RedisPrefixConstant.PG_STATISTIC, sumRedisKey, summaryDisplay, RedisTimeConstant.TEN_MINUTES);
            }
            object.put("summaryDisplay", summaryDisplay);
        }
        String statisValue = object.toJSONString();
        return statisValue;
    }

    /**
     * 计算数字字段的合计
     * @param index
     * @param type
     * @param summaryAttrList 字段列表
     * @param searchQuery 检索条件
     * @return
     * index type 核对完成
     */
    public List<AggSumVO> aggSumByField(String index, String type, List<AggSumDTO> summaryAttrList, NativeSearchQuery searchQuery) throws XbbException {
        List<AggSumVO> summaryDisplay = new ArrayList<>();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = (BoolQueryBuilder) searchQuery.getQuery();
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, type));
        //设置查询条件
        sourceBuilder.query(boolQueryBuilder);
        sourceBuilder.size(0);
        List<AggSumDTO> sumList = new ArrayList<>();
        //LinkedHashMap防止k-v不对应
        Map<String,String> summaryAttrMap = new LinkedHashMap<>();
        for (AggSumDTO aggSumDTO : summaryAttrList) {
            String alias = aggSumDTO.getAttr();
            String name = aggSumDTO.getName();
            if(!StringUtil.isEmpty(alias)){
                summaryAttrMap.put(alias, name);
                sumList.add(aggSumDTO);
            }
        }
        for (int j = 0; j < sumList.size(); j++) {
            AggSumDTO aggSumDTO = sumList.get(j);
            SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggSumDTO.getAttr()).field(aggSumDTO.getField());
            sourceBuilder.aggregation(sumAggregationBuilder);
        }
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        JSONObject aggJson = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        Integer sumTotal = aggJson.getJSONObject("hits").getInteger("total");
        if(sumTotal !=null && sumTotal > 0) {
            JSONObject aggJsonObject = aggJson.getJSONObject("aggregations");
            if (aggJsonObject != null && !aggJsonObject.isEmpty()){
                for (Map.Entry<String, String> entry : summaryAttrMap.entrySet()) {
                    String attr = entry.getKey();
                    String attrName = entry.getValue();
                    AggSumVO aggSumVO = new AggSumVO();
                    if("totalNum".equals(attr)) {
                        //业务总数--直接用pageHelper即可
                        //在外面处理
                    }else {
                        //  attrName, aggJsonObject.getJSONObject(attr).getDouble("value")
                        aggSumVO.setAttr(attr);
                        aggSumVO.setName(attrName);
                        aggSumVO.setValue(aggJsonObject.getJSONObject(attr).get("value"));
                        summaryDisplay.add(aggSumVO);
                    }
                }
            }
            //业务总数--直接用pageHelper即可
            String attrName = summaryAttrMap.get("total_num");
            if(!StringUtil.isEmpty(attrName)) {
                AggSumVO aggSumVO = new AggSumVO();
                aggSumVO.setName(attrName);
                aggSumVO.setValue(sumTotal);
                summaryDisplay.add(aggSumVO);
            }
        }
        return summaryDisplay;
    }

    /**
     * 库存查询成本汇总专用
     * @param index
     * @param summaryAttrList 字段列表
     * @param searchQuery 检索条件
     * @return
     * index 核对完成
     */
    public List<AggSumVO> aggStockSumByField(String index, List<AggSumDTO> summaryAttrList, NativeSearchQuery searchQuery) throws XbbException {
        List<AggSumVO> summaryDisplay = new ArrayList<>();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //设置查询条件
        sourceBuilder.query(searchQuery.getQuery());
        sourceBuilder.size(0);
        List<AggSumDTO> sumList = new ArrayList<>();
        //LinkedHashMap防止k-v不对应
        Map<String,String> summaryAttrMap = new LinkedHashMap<>();
        for (AggSumDTO aggSumDTO : summaryAttrList) {
            String alias = aggSumDTO.getAttr();
            String name = aggSumDTO.getName();
            if(!StringUtil.isEmpty(alias)){
                summaryAttrMap.put(alias, name);
                sumList.add(aggSumDTO);
            }
        }
        for (int j = 0; j < sumList.size(); j++) {
            SumAggregationBuilder sumAggregationBuilder;
            AggSumDTO aggSumDTO = sumList.get(j);
            if (Objects.equals(aggSumDTO.getAttr(), ProductWarehouseSubFormEnum.COST.getDataAttr())) {
                sumAggregationBuilder = AggregationBuilders.sum(aggSumDTO.getAttr()).script(new Script("doc['" + ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.NUM) + "'].value * doc['" + ProductWarehouseSubFormEnum.getAttrConnectData(ProductWarehouseSubFormEnum.COST) + "'].value"));
            } else {
                sumAggregationBuilder = AggregationBuilders.sum(aggSumDTO.getAttr()).field(aggSumDTO.getField());
            }

            sourceBuilder.aggregation(sumAggregationBuilder);
        }
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        JSONObject aggJson = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        Integer sumTotal = aggJson.getJSONObject("hits").getInteger("total");
        if(sumTotal !=null && sumTotal > 0) {
            JSONObject aggJsonObject = aggJson.getJSONObject("aggregations");
            if (aggJsonObject != null && !aggJsonObject.isEmpty()){
                for (Map.Entry<String, String> entry : summaryAttrMap.entrySet()) {
                    String attr = entry.getKey();
                    String attrName = entry.getValue();
                    AggSumVO aggSumVO = new AggSumVO();
                    if("totalNum".equals(attr)) {
                        //业务总数--直接用pageHelper即可
                        //在外面处理
                    }else {
                        //  attrName, aggJsonObject.getJSONObject(attr).getDouble("value")
                        aggSumVO.setAttr(attr);
                        aggSumVO.setName(attrName);
                        aggSumVO.setValue(aggJsonObject.getJSONObject(attr).get("value"));
                        summaryDisplay.add(aggSumVO);
                    }
                }
            }
            //业务总数--直接用pageHelper即可
            String attrName = summaryAttrMap.get("total_num");
            if(!StringUtil.isEmpty(attrName)) {
                AggSumVO aggSumVO = new AggSumVO();
                aggSumVO.setName(attrName);
                aggSumVO.setValue(sumTotal);
                summaryDisplay.add(aggSumVO);
            }
        }
        return summaryDisplay;
    }


    /**
     * 求平均值
     * @param index
     * @param type
     * @param summaryAttrList
     * @param searchQuery
     * @return
     * type 核对完成
     */
    public List<AggSumVO> aggAvgByField(String index, String type, List<AggSumDTO> summaryAttrList,NativeSearchQuery searchQuery) throws XbbException {
        List<AggSumVO> summaryDisplay = new ArrayList<>();
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //设置查询条件
        BoolQueryBuilder queryBuilder = (BoolQueryBuilder) searchQuery.getQuery();
        queryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,type));
        sourceBuilder.query(queryBuilder);
        sourceBuilder.size(0);
        List<AggSumDTO> sumList = new ArrayList<>();
        //LinkedHashMap防止k-v不对应
        Map<String,String> summaryAttrMap = new LinkedHashMap<>();
        for (AggSumDTO aggSumDTO : summaryAttrList) {
            String alias = aggSumDTO.getAttr();
            String name = aggSumDTO.getField();
            if(!StringUtil.isEmpty(alias)){
                summaryAttrMap.put(alias, name);
                sumList.add(aggSumDTO);
            }
        }
        for (int j = 0; j < sumList.size(); j++) {
            AggSumDTO aggSumDTO = sumList.get(j);
            AvgAggregationBuilder avgAggregationBuilder = AggregationBuilders.avg(aggSumDTO.getAttr()).field(aggSumDTO.getField());
            sourceBuilder.aggregation(avgAggregationBuilder);
        }
        //执行查询 返回结果
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        JSONObject aggJson = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        Integer sumTotal = aggJson.getJSONObject("hits").getInteger("total");
        if(sumTotal !=null && sumTotal > 0) {
            JSONObject aggJsonObject = aggJson.getJSONObject("aggregations");
            if (aggJsonObject != null && !aggJsonObject.isEmpty()){
                for (Map.Entry<String, String> entry : summaryAttrMap.entrySet()) {
                    String attr = entry.getKey();
                    String attrName = entry.getValue();
                    AggSumVO aggSumVO = new AggSumVO();
                    if("totalNum".equals(attr)) {
                        //业务总数--直接用pageHelper即可
                        //在外面处理
                    }else {
                        aggSumVO.setAttr(attr);
                        aggSumVO.setName(attrName);
                        Double value = 0D;
                        if (Objects.nonNull(aggJsonObject.get(attr)) && aggJsonObject.get(attr) instanceof Map && aggJsonObject.getJSONObject(attr).containsKey("value")) {
                            value = aggJsonObject.getJSONObject(attr).getDouble("value");
                            if (Objects.isNull(value)) {
                                value = 0D;
                            }
                        }
                        aggSumVO.setValue(StringUtil.formatDecimal(value*100) + "%");
                        summaryDisplay.add(aggSumVO);
                    }
                }
            }
        }
        return summaryDisplay;
    }

    /**
     * 获取排重结果
     * @param topHitsDTO
     * @return
     * indexTypeEnum 核对完成
     */
    public List<PaasFormDataEntityExt> topHits(TopHitsDTO topHitsDTO, IndexTypeEnum indexTypeEnum) {
        return getPaasFormDataEntityExts(topHitsDTO,PaasFormDataEntityExt.class, indexTypeEnum);
    }

    /**
     * 获取排重结果(返回类型外部传入)
     * @param topHitsDTO
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> List<T> getPaasFormDataEntityExts(TopHitsDTO topHitsDTO, Class<T> clazz, IndexTypeEnum indexTypeEnum ) {
        List<T> list = new ArrayList<>();
        try {
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
            sourceBuilder.size(0);
            String field = topHitsDTO.getField();
            Integer size = topHitsDTO.getSource() == null || topHitsDTO.getSource().isEmpty() ? 2 : topHitsDTO.getSource().size() + 1;
            String[] sourceArray = new String[size];
            sourceArray[0] = "dataId";
            if (topHitsDTO.getSource() == null || topHitsDTO.getSource().isEmpty()){
                sourceArray[1] = field;
            }else {
                Integer index = 1;
                for (String source : topHitsDTO.getSource()){
                    sourceArray[index] = source;
                    index++;
                }
            }
            //text类型的分组要加.keyword
            if (StringUtil.isNotEmpty(field)) {
                if(FieldTypeEnum.TEXT.getEsalias().equals(topHitsDTO.getFieldTypeEnum().getEsalias())){
                    field = field + ".keyword";
                }else if(FieldTypeEnum.COMBO_CHECK.getEsalias().equals(topHitsDTO.getFieldTypeEnum().getEsalias())){
                    field = field + ".keyword";
                }else if(FieldTypeEnum.ADDRESS.getEsalias().equals(topHitsDTO.getFieldTypeEnum().getEsalias())){
                    field = field + ".address.keyword";
                }
            }

            String sortField = "updateTime";
            BucketOrder order = BucketOrder.aggregation("aggs021", false);
            if (StringUtil.isNotEmpty(field)) {
                TermsAggregationBuilder termsAggregation = AggregationBuilders.terms("aggs01").field(field).size(topHitsDTO.getSize()).order(order);
                termsAggregation.executionHint(ElasticConstant.EXECUTION_HINT);
                TopHitsAggregationBuilder topHitsAggregationBuilder = AggregationBuilders.topHits("aggs02").size(1).fetchSource(sourceArray,new String[0]).sort(sortField, SortOrder.DESC);
                //为了排序
                MaxAggregationBuilder maxAggregationBuilder = AggregationBuilders.max("aggs021").field(sortField);
                termsAggregation.subAggregation(topHitsAggregationBuilder);
                termsAggregation.subAggregation(maxAggregationBuilder);
                sourceBuilder.aggregation(termsAggregation);
            } else {
                TopHitsAggregationBuilder topHitsAggregationBuilder = AggregationBuilders.topHits("aggs01").size(100).fetchSource(sourceArray,new String[0]).sort(sortField,SortOrder.DESC);
                sourceBuilder.aggregation(topHitsAggregationBuilder);
            }

            //执行查询 返回结果
            //设置查询路由
            PaasFormDataEsMappingDTO paasFormDataEsMappingDTO = new PaasFormDataEsMappingDTO();
            BeanUtil.copyProperties(topHitsDTO,paasFormDataEsMappingDTO);
            // 获取es的index和type
            //设置查询条件
            BoolQueryBuilder query = (BoolQueryBuilder) topHitsDTO.getSearchQuery().getQuery();
            sourceBuilder.query(query);
            query.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
            SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
            searchRequest.source(sourceBuilder);
            JSONObject aggJson = xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
            JSONArray resultArray = aggJson.getJSONObject("aggregations").getJSONObject("aggs01").getJSONArray("buckets");
            if(resultArray != null && !resultArray.isEmpty()){
                for (Object object : resultArray){
                    if(object != null){
                        JSONObject json = (JSONObject) object;
                        JSONObject jsonObject =  (JSONObject) json.getJSONObject("aggs02").getJSONObject("hits").getJSONArray("hits").get(0);
                        list.add(JSONObject.parseObject(JSON.toJSONString(jsonObject.getJSONObject("_source")),clazz));
                    }
                }
            }
        }catch (Exception e){
            LOG.error("获取排重数据出错", e);
        }
        return list;
    }

    private void setSumArray(JSONArray summaryDisplay, String attrName, Object value) {
        JSONObject json = new JSONObject();
        json.put("name", attrName);
        json.put("value", value);
        summaryDisplay.add(json);
    }
    /**
     * 批量操作（可进行批量插入或更新）
     * @param client
     * @param bulkRequest
     * @throws XbbException
     */
    public  void bulk(Client client,BulkRequest bulkRequest) throws XbbException {
        try {
            client.bulk(bulkRequest).get();
        } catch (Exception e) {
            LOG.error("es报错",e);
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, SystemErrorCodeEnum.API_ERROR_100001.getMsg());
        }
    }

    /***
     * 获取索引枚举
     * es index 处理完 by wufeng @date 2022/5/6 15:02
     * @param paasFormDataEsMappingDTO
     * @return
     * @throws XbbException
     */
    @Deprecated
    public static IndexTypeEnum getIndexTypeEnum(PaasFormDataEsMappingDTO paasFormDataEsMappingDTO) throws XbbException {
        //此处可以把变量写在前面，因为前面已经做了非空验证
        return getIndexTypeEnum(paasFormDataEsMappingDTO.getSaasMark(), paasFormDataEsMappingDTO.getBusinessType());
    }
    @Deprecated
    public static IndexTypeEnum getIndexTypeEnum(Integer saasMark, Integer businessType) throws XbbException {
       return EsUtil.getIndexTypeEnum(saasMark,businessType);
    }

    /**
     * 封装查询 SearchRequest
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param sortBuilderList 排序list，可传空。内部可塞入new FieldSortBuilder(sortField).order(order)
     * @return org.elasticsearch.action.search.SearchRequest
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    private SearchRequest getSearchRequest(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, List<String> fieldList, List<SortBuilder> sortBuilderList) {
        //查询步长默认设置为10000
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD, indexTypeEnum.getType()));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        EsUtil.setPage(sourceBuilder, 1, SizeConstant.PAGE_SIZE);
        if (sortBuilderList != null) {
            for (SortBuilder sortBuilder : sortBuilderList) {
                sourceBuilder.sort(sortBuilder);
            }
        }
        //处理显示字段
        EsUtil.setFieldListNoMerge(sourceBuilder, fieldList);
        searchRequest.source(sourceBuilder);
        //输出日志
        String prefix = "------------------findByScroll------------------:";
        StringBuilder syso = setStringBuilder4Log(indexTypeEnum, prefix);
        LOG.info(syso.toString());
        return searchRequest;
    }
    /**
     * 穿透导出使用，限制5w
     * 用游标方式查询es数据（支持排序）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param clazz 业务实体class
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param sortBuilderList 排序list，可传空。内部可塞入new FieldSortBuilder(sortField).order(order)
     * @return java.util.List
     * @throws XbbException
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public List findByScroll4Export(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, Class clazz, List<String> fieldList, List<SortBuilder> sortBuilderList) throws XbbException {
        SearchRequest searchRequest = getSearchRequest(indexTypeEnum, boolQueryBuilder, fieldList, sortBuilderList);
        return xbbElasticsearchRestTemplate.getScrollListForExport(clazz, searchRequest);
    }
    /**
     * 用游标方式查询es数据（不排序）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param clazz 业务实体class
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @return java.util.List 返回对应实体的list
     * @throws XbbException
     * @author zcp
     * @date 2019/2/22 14:40
     * @lastUpdateDate 2022/5/5 16:14 indexTypeEnum核对完成 by wufeng
     * @since v1.0
     * @version v1.0
     */
    public List findByScroll(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, Class clazz, List<String> fieldList) throws XbbException {
        return findByScroll(indexTypeEnum, boolQueryBuilder, clazz, fieldList, null);
    }

    /**
     * 用游标方式查询es数据（支持排序）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param clazz 业务实体class
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param sortBuilderList 排序list，可传空。内部可塞入new FieldSortBuilder(sortField).order(order)
     * @return java.util.List
     * @throws XbbException
     * @author zcp
     * @date 2019/6/4 14:20
     * @lastUpdateDate indexTypeEnum核对完成 by wufeng @date 2022/5/6 15:04
     * @since v1.0
     * @version v1.0
     */
    public List findByScroll(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, Class clazz, List<String> fieldList, List<SortBuilder> sortBuilderList) throws XbbException {
        SearchRequest searchRequest = getSearchRequest(indexTypeEnum, boolQueryBuilder, fieldList, sortBuilderList);
        long scrollTimeInMillis = SizeConstant.SCROLL_TIME_IN_MILLIS;
        return xbbElasticsearchRestTemplate.findScrollList(clazz,searchRequest,scrollTimeInMillis);
    }

    /**
     * 按照分页查询list
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param clazz 业务实体class
     * @param fieldList 查询字段list，用于限定返回值，如果不传则默认查出全部字段
     * @param page 页码
     * @param pageSize 当页记录数
     * @param sortBuilderList 排序list，可传空。内部可塞入new FieldSortBuilder(sortField).order(order)
     * @return org.springframework.data.elasticsearch.core.aggregation.AggregatedPage
     * （1）返回值可能为null；（2）esEntities.getContent()获取到相应list；（3）esEntities.getTotalElements()得到总数
     * @throws XbbException
     * @author zcp
     * @date 2019/4/9 20:18
     * @lastUpdateDate indexTypeEnum核对完成 by wufeng @date 2022/5/6 15:04
     * @since v1.0
     * @version v1.0
     */
    public XbbAggregatedPage findByPage(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, Class clazz, List<String> fieldList, Integer page, Integer pageSize, List<SortBuilder> sortBuilderList) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
        sourceBuilder.query(boolQueryBuilder);
        PageRequest pageRequest = EsUtil.setPage(sourceBuilder, page, pageSize);
        //处理显示字段
        EsUtil.setFieldListNoMerge(sourceBuilder, fieldList);
        if (sortBuilderList != null) {
            for (SortBuilder sortBuilder : sortBuilderList) {
                sourceBuilder.sort(sortBuilder);
            }
        }
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        searchRequest.source(sourceBuilder);
        return xbbElasticsearchRestTemplate.queryForPages(pageRequest,searchRequest, clazz);
    }

    /**
     * 聚合通用（聚合条件外部传入），只获取出最外层的total和aggregations
     * 单个聚合条件
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.alibaba.fastjson.JSONObject 返回JSONObject，格式为：{"total":1,"aggregations":{}}
     * @author zcp
     * @date 2019/2/20 22:40
     * @lastUpdateDate indexTypeEnum核对完成 by wufeng @date 2022/5/6 15:04
     * @since v1.0
     * @version v1.0
     */
    public JSONObject elasticAggs4NoBuckets2Object(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        List<ValuesSourceAggregationBuilder> aggsList = new ArrayList<>();
        if (valuesSourceAggregationBuilder != null) {
            aggsList.add(valuesSourceAggregationBuilder);
        }

        /*
         返回结果形式为：
         （1）单层时间分桶聚合
          "aggregations": {
            "aggs01": {
              "buckets": [{
                  "key_as_string": "2019-01-01",
                  "key": 1546272000000,
                  "doc_count": 0
                }]
             }
          }
         （2）单层cardinality聚合
          "aggregations": {
            "aggs01": {
              "value": 0
            }
          }
         （3）双层聚合（外层时间，内层cardinality）
          "aggregations": {
            "aggs01": {
              "buckets": [{
                  "key_as_string": "2019-02-01",
                  "key": 1548950400000,
                  "doc_count": 3,
                  "aggs02": {
                    "value": 0
                  }
                }]
             }
           }
        */
        return multElasticAggs4NoBuckets2Object(indexTypeEnum, boolQueryBuilder, aggsList);
    }
    /**
     * 多聚合条件的聚合结果
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param aggsList 聚合条件
     * @return com.alibaba.fastjson.JSONObject
     * @author zcp
     * @date 2019/4/19 16:21
     * @since v1.0
     * @version v1.0
     */
    public JSONObject multElasticAggs4NoBuckets2Object(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, List<ValuesSourceAggregationBuilder> aggsList) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        boolQueryBuilder.filter(termQuery(ElasticConstant.MY_JOIN_FIELD,indexTypeEnum.getType()));
        //设置查询条件、聚合条件
        sourceBuilder.query(boolQueryBuilder);
        for (ValuesSourceAggregationBuilder agg : aggsList) {
            sourceBuilder.aggregation(agg);
        }
        sourceBuilder.size(0);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        searchRequest.source(sourceBuilder);
        //执行查询，得到结果
        JSONObject responseObject =  xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        JSONObject aggregations = responseObject.getJSONObject("aggregations");
        if (aggregations == null) {
            //为空则初始化
            aggregations = new JSONObject();
        }
        Long total = 0L;
        if(responseObject.getJSONObject("hits") != null){
            total = responseObject.getJSONObject("hits").getLong("total");
        }
        long totalLong = total == null ? 0L : total;
        JSONObject resultJsonObject = new JSONObject();
        resultJsonObject.put("aggregations", aggregations);
        resultJsonObject.put("total", totalLong);
        return resultJsonObject;
    }

    /**
     * 多聚合条件并进行分组的聚合结果(高危接口，架构组已经同意呼叫中心使用，限制分组100)
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param aggsList 聚合条件
     * @return com.alibaba.fastjson.JSONObject
     * @author zcp
     * @date 2019/4/19 16:21
     * @since v1.0
     * @version v1.0
     */
    public JSONObject multElasticAggs4Buckets2User(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, List<ValuesSourceAggregationBuilder> aggsList, String fieldAttr, boolean sortType) throws XbbException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //设置查询条件、聚合条件
        sourceBuilder.query(boolQueryBuilder);

        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("userId.keyword").field("userId.keyword");
        termsAggregationBuilder.size(ChartConstant.CHART_AGG_SIZE);
        BucketOrder order = BucketOrder.aggregation(fieldAttr, sortType);
        termsAggregationBuilder.order(order);
        for (ValuesSourceAggregationBuilder agg : aggsList) {
            termsAggregationBuilder.subAggregation(agg);
        }
        sourceBuilder.aggregation(termsAggregationBuilder);

        sourceBuilder.size(0);
        SearchRequest searchRequest = new SearchRequest(indexTypeEnum.getIndex());
        searchRequest.source(sourceBuilder);
        //执行查询，得到结果
        JSONObject responseObject =  xbbElasticsearchRestTemplate.getAggResponseJson(searchRequest);
        JSONObject aggregations = responseObject.getJSONObject("aggregations");
        if (aggregations == null) {
            //为空则初始化
            aggregations = new JSONObject();
        }
        Long total = responseObject.getJSONObject("hits").getLong("total");
        long totalLong = total == null ? 0L : total;
        JSONObject resultJsonObject = new JSONObject();
        resultJsonObject.put("aggregations", aggregations);
        resultJsonObject.put("total", totalLong);
        return resultJsonObject;
    }
    /**
     * 同时sum多个字段（同一级的字段）的结果，比如对账单同时汇总应收金额、核销金额
     * @param sumFieldList 要sum的字段attr，同时也作为其分组key
     * @return java.util.List<org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder>
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public JSONObject sumMuchField4Some(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, List<String> sumFieldList) throws XbbException {
        List<ValuesSourceAggregationBuilder> aggsList = new ArrayList<>();
        for (String sumField : sumFieldList) {
            //聚合字段和聚合分组的key都使用sumField
            SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(sumField).field(sumField);
            aggsList.add(sumAggregationBuilder);
        }
        JSONObject jsonObject = multElasticAggs4NoBuckets2Object(indexTypeEnum, boolQueryBuilder, aggsList);
        JSONObject aggregations = jsonObject.getJSONObject("aggregations");
        JSONObject result = new JSONObject();
        for (String sumField : sumFieldList) {
            JSONObject sumObj = aggregations.getJSONObject(sumField);
            double value = 0D;
            if (Objects.nonNull(sumObj)) {
                value = sumObj.getDoubleValue("value");
            }
            result.put(sumField, value);
        }
        return result;
    }


    public static void main(String[] args) {
        String str= "{\"took\":3,\"timed_out\":false,\"_shards\":{\"total\":5,\"successful\":5,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":110,\"max_score\":0.0,\"hits\":[]},\"aggregations\":{\"date_histogram#aggs01\":{\"buckets\":[{\"key_as_string\":\"2019-06-01\",\"key\":1559318400000,\"doc_count\":1,\"sum#aggs02\":{\"value\":156.0}},{\"key_as_string\":\"2019-06-02\",\"key\":1559404800000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-03\",\"key\":1559491200000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-04\",\"key\":1559577600000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-05\",\"key\":1559664000000,\"doc_count\":2,\"sum#aggs02\":{\"value\":123.0}},{\"key_as_string\":\"2019-06-06\",\"key\":1559750400000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-07\",\"key\":1559836800000,\"doc_count\":1,\"sum#aggs02\":{\"value\":222.0}},{\"key_as_string\":\"2019-06-08\",\"key\":1559923200000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-09\",\"key\":1560009600000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-10\",\"key\":1560096000000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-11\",\"key\":1560182400000,\"doc_count\":2,\"sum#aggs02\":{\"value\":245.0}},{\"key_as_string\":\"2019-06-12\",\"key\":1560268800000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-13\",\"key\":1560355200000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-14\",\"key\":1560441600000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-15\",\"key\":1560528000000,\"doc_count\":1,\"sum#aggs02\":{\"value\":22.56}},{\"key_as_string\":\"2019-06-16\",\"key\":1560614400000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-17\",\"key\":1560700800000,\"doc_count\":10,\"sum#aggs02\":{\"value\":98.0}},{\"key_as_string\":\"2019-06-18\",\"key\":1560787200000,\"doc_count\":5,\"sum#aggs02\":{\"value\":-2.0}},{\"key_as_string\":\"2019-06-19\",\"key\":1560873600000,\"doc_count\":2,\"sum#aggs02\":{\"value\":1.00000001E10}},{\"key_as_string\":\"2019-06-20\",\"key\":1560960000000,\"doc_count\":3,\"sum#aggs02\":{\"value\":-1.0E10}},{\"key_as_string\":\"2019-06-21\",\"key\":1561046400000,\"doc_count\":16,\"sum#aggs02\":{\"value\":12158.12}},{\"key_as_string\":\"2019-06-22\",\"key\":1561132800000,\"doc_count\":15,\"sum#aggs02\":{\"value\":17961.719999999998}},{\"key_as_string\":\"2019-06-23\",\"key\":1561219200000,\"doc_count\":1,\"sum#aggs02\":{\"value\":0.0}},{\"key_as_string\":\"2019-06-24\",\"key\":1561305600000,\"doc_count\":9,\"sum#aggs02\":{\"value\":1.2598705479999999E7}},{\"key_as_string\":\"2019-06-25\",\"key\":1561392000000,\"doc_count\":15,\"sum#aggs02\":{\"value\":1013.8}},{\"key_as_string\":\"2019-06-26\",\"key\":1561478400000,\"doc_count\":8,\"sum#aggs02\":{\"value\":1883.0}},{\"key_as_string\":\"2019-06-27\",\"key\":1561564800000,\"doc_count\":4,\"sum#aggs02\":{\"value\":155.0}},{\"key_as_string\":\"2019-06-28\",\"key\":1561651200000,\"doc_count\":14,\"sum#aggs02\":{\"value\":1023869.0}},{\"key_as_string\":\"2019-06-29\",\"key\":1561737600000,\"doc_count\":1,\"sum#aggs02\":{\"value\":38291.0}},{\"key_as_string\":\"2019-06-30\",\"key\":1561824000000,\"doc_count\":0,\"sum#aggs02\":{\"value\":0.0}}]}}}";
        JSONObject responseObject = JSONObject.parseObject(str.toString());
        JSONObject aggregations = responseObject.getJSONObject("aggregations");
        JSONObject newAggregations =new JSONObject();
        for (Map.Entry<String, Object> stringObjectEntry : aggregations.entrySet()) {
            String key = stringObjectEntry.getKey();
            key = key.substring(key.indexOf("#")+1,key.length() );
            newAggregations.put(key, stringObjectEntry.getValue());
            JSONObject jsonObject= (JSONObject) stringObjectEntry.getValue();
            JSONArray buckets = jsonObject.getJSONArray("buckets");
            if(!buckets.isEmpty()){
                for (Object object : buckets) {
                    JSONObject bucket = (JSONObject) object;
                }
            }
        }
    }


    /**
     * 聚合通用方法（聚合条件外部传入），获取到最外层的total和第一层aggs01内的buckets
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.alibaba.fastjson.JSONObject 返回的是JSONObject，格式为：{"total":4,"buckets":[{"key_as_string":"2019-01-01","doc_count":0,"key":1546272000000},{"key_as_string":"2019-01-02","doc_count":0,"key":1546358400000},{"key_as_string":"2019-01-31","doc_count":2,"key":1548864000000}]}
     * @author zcp
     * @date 2019/2/20 21:29
     * @lastTime indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public JSONObject elasticAggs4Buckets2Object(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        JSONObject jsonObject = elasticAggs4NoBuckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        //此时得到的aggregations不为空，内部已经处理
        JSONObject aggregations = jsonObject.getJSONObject("aggregations");
        Long total = jsonObject.getLong("total");

        JSONArray bucketsArray = aggregations.getJSONObject("aggs01") == null ? new JSONArray() : aggregations.getJSONObject("aggs01").getJSONArray("buckets");
        bucketsArray = bucketsArray == null ? new JSONArray() : bucketsArray;
        /*
         格式化结果，最终返回数据格式为：
         {
             "total":4,
             "buckets":[{"key_as_string":"2019-01-01","doc_count":0,"key":1546272000000},{"key_as_string":"2019-01-02","doc_count":0,"key":1546358400000},{"key_as_string":"2019-01-31","doc_count":2,"key":1548864000000}]
         }
         buckets可能有二种形式：（其中单层cardinality聚合: []，不在该方法处理）
         （1）单层时间分桶聚合
              "buckets": [{
                  "key_as_string": "2019-01-01",
                  "key": 1546272000000,
                  "doc_count": 0
                }]
         （2）双层聚合（外层时间，内层cardinality）
              "buckets": [{
                  "key_as_string": "2019-02-01",
                  "key": 1548950400000,
                  "doc_count": 3,
                  "aggs02": {
                    "value": 0
                  }
                }]
         */
        JSONObject resultJsonObject = new JSONObject();
        resultJsonObject.put("buckets", bucketsArray);
        resultJsonObject.put("total", total);
        return resultJsonObject;
    }



    /**
     * 聚合通用方法（聚合条件外部传入），读取到第一层aggs01内buckets的key和doc_count（未获取第二层）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.paas.elasticsearch.pojo.vo.ElasticAggsPojo 返回查询结果
     * @author zcp
     * @date 2019/2/14 18:13
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public ElasticAggsPojo elasticAggs(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        Long total = resultJsonObject.getLong("total");
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                Integer count = json.getInteger("doc_count");
                String value = count == null ? "0" : count.toString();
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(json.getString("key"), value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
            }
        }
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        elasticAggsPojo.setTotal(String.valueOf(total));
        return elasticAggsPojo;
    }
    /**
     * 聚合通用方法（聚合条件外部传入），读取到第一层aggs01内buckets的key和第二层aggs02内的value（未获取第二层）
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.paas.elasticsearch.pojo.vo.ElasticAggsPojo 返回查询结果
     * @author zcp
     * @date 2019/2/21 13:34
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public ElasticAggsPojo elasticAggsSub(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                //第一层的key
                String key = json.getString("key");
                Integer count = json.getJSONObject("aggs02").getInteger("value");
                String value = count == null ? "0" : count.toString();
                //内部塞第一层的key（时间桶），value则读取第二层aggs02内的value（排重后的结果）
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
            }
        }
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        //排重的不能通过for循环把每个时间桶内的值求和：因为各个桶之间没有进行排重，所以总数的要另外计算
        elasticAggsPojo.setTotal("");
        return elasticAggsPojo;
    }
    /**
     * 分桶求和统计int型字段
     * 三层聚合：第一层时间分桶，第二层int型字段求和
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.pro.statistic.result.pojo.ElasticAggsPojo
     * @author zcp
     * @date 2019/6/3 19:13
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public ElasticAggsPojo elasticAggs4Int(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        int total = 0;
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                //第二层的结果
                int perValue = json.getJSONObject("aggs02").getIntValue("value");
                total += perValue;

                //第一层的key，时间
                String key = json.getString("key");
                String value = String.valueOf(perValue);
                //内部塞第一层的key（时间桶），value则读取第二层aggs02各个桶内aggs03内所有value转为人民币的总和
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
            }
        }
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        //排重的不能通过for循环把每个时间桶内的值求和：因为各个桶之间没有进行排重，所以总数的要另外计算
        elasticAggsPojo.setTotal(String.valueOf(total));
        return elasticAggsPojo;
    }
    /**
     * 金额类聚合通用方法（时间分桶）
     * 三层聚合：第一层时间分桶，第二层double型字段求和
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.paas.elasticsearch.pojo.ElasticAggsPojo
     * @author youli.chen
     * @date 2019/2/27 15:25
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public ElasticAggsPojo elasticAggs4Double(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder,
                                              ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        double total = 0;
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                //第二层的结果
                Double perAmount = json.getJSONObject("aggs02").getDoubleValue("value");
                //保留2位小数
                perAmount = Arith.div(perAmount, 1, 2);

                //第一层的key，时间
                String key = json.getString("key");
                String value = String.valueOf(perAmount);
                //内部塞第一层的key（时间桶），value则读取第二层aggs02各个桶内aggs03内所有value转为人民币的总和
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
                total += perAmount;
            }
        }
        //保留2位小数
        total = Arith.div(total, 1, 2);
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        //排重的不能通过for循环把每个时间桶内的值求和：因为各个桶之间没有进行排重，所以总数的要另外计算
        elasticAggsPojo.setTotal(String.valueOf(total));
        return elasticAggsPojo;
    }

    /**
     * 第三层聚合的结果
     * 第一层：时间，第三层：结果值
     *
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.pro.statistic.result.pojo.ElasticAggsPojo
     * @author 徐俊杰
     * @date 2019/4/26 8:54
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     */
    public ElasticAggsPojo elasticAggs4Three(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder,
                                             ValuesSourceAggregationBuilder valuesSourceAggregationBuilder, Long refId) throws XbbException {
        //获取结果
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        double total = 0;
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        //处理buckets
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                JSONObject aggs02 = json.getJSONObject("aggs02");
                JSONObject aggs03 = Objects.nonNull(aggs02) ? aggs02.getJSONObject("filter#aggs03") : null;
                double perAmount = 0;
                if (Objects.nonNull(aggs03)) {
                    JSONObject aggs04 = aggs03.getJSONObject("aggs04");
                    perAmount = (Objects.isNull(aggs04)) ? 0D : aggs04.getDoubleValue("value");
                }
                //保留2位小数
                perAmount = Arith.div(perAmount, 1, 2);
                //第一层的key，时间
                String key = json.getString("key");
                //第三层的结果
                String value = String.valueOf(perAmount);
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
                total += perAmount;
            }
        }
        //保留2位小数
        total = Arith.div(total, 1, 2);
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        elasticAggsPojo.setTotal(String.valueOf(total));
        return elasticAggsPojo;
    }

    /**
     * 处理第二层同时求和两个字段的聚合结果（包含产品数量之和，产品入库数量之和）
     * 两层聚合：1.id，2.1.产品数量，2.2.产品入库数量
     *
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @return com.xbongbong.paas.elasticsearch.pojo.ElasticAggsPojo
     * @author 徐俊杰
     * @date 2019/3/29 11:20
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     */
    public ElasticAggsPojo elasticAggs4DoubleTwoSum(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder,
                                                    ValuesSourceAggregationBuilder valuesSourceAggregationBuilder) throws XbbException {
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        for (Object object : bucketsArray) {
            if (object != null) {
                List<String> valueList = new ArrayList<>();
                //第一层的key，分组id
                JSONObject json = (JSONObject) object;
                String key = json.getString("key");
                //第二层的第一个结果
                double amount1 = json.getJSONObject("aggs021").getDoubleValue("value");
                //保留2位小数
                amount1 = Arith.div(amount1, 1, 2);
                String value1 = String.valueOf(amount1);
                valueList.add(value1);
                //第二层的第二个结果
                double amount2 = json.getJSONObject("aggs022").getDoubleValue("value");
                //保留2位小数
                amount2 = Arith.div(amount2, 1, 2);
                String value2 = String.valueOf(amount2);
                valueList.add(value2);
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, valueList);
                aggsBucketsPojoList.add(aggsBucketsPojo);
            }
        }
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        return elasticAggsPojo;
    }
    /**
     * 金额类聚合通用方法（时间分桶）
     * 三层聚合：第一层时间分桶，第二层币种分组，第三层金额求和
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param valuesSourceAggregationBuilder 聚合条件
     * @param currencyRateMap 币种id--币种汇率
     * @return com.xbongbong.paas.elasticsearch.pojo.ElasticAggsPojo
     * @author zcp
     * @date 2019/2/27 15:25
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public ElasticAggsPojo elasticAggs4Amount(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, ValuesSourceAggregationBuilder valuesSourceAggregationBuilder, Map<Long, Double> currencyRateMap) throws XbbException {
        JSONObject resultJsonObject = elasticAggs4Buckets2Object(indexTypeEnum, boolQueryBuilder, valuesSourceAggregationBuilder);
        ElasticAggsPojo elasticAggsPojo = new ElasticAggsPojo();
        List<AggsBucketsPojo> aggsBucketsPojoList = new ArrayList<>();
        double total = 0;
        JSONArray bucketsArray = resultJsonObject.getJSONArray("buckets");
        for (Object object : bucketsArray) {
            if (object != null) {
                JSONObject json = (JSONObject) object;
                //第二层的结果
                JSONArray jsonArrayTwo = json.getJSONObject("aggs02").getJSONArray("buckets");
                //每个桶内（各个币种）内总金额（转为人民币）
                double perAmount = 0;
                for (Object objectTwo : jsonArrayTwo) {
                    double amount = getBucketsAmount(currencyRateMap, objectTwo, "aggs03");
                    perAmount += amount;
                    total += amount;
                }
                //保留2位小数
                perAmount = Arith.div(perAmount, 1, 2);

                //第一层的key，时间
                String key = json.getString("key");
                String value = String.valueOf(perAmount);
                //内部塞第一层的key（时间桶），value则读取第二层aggs02各个桶内aggs03内所有value转为人民币的总和
                AggsBucketsPojo aggsBucketsPojo = new AggsBucketsPojo(key, value);
                aggsBucketsPojoList.add(aggsBucketsPojo);
            }
        }
        elasticAggsPojo.setBuckets(aggsBucketsPojoList);
        //排重的不能通过for循环把每个时间桶内的值求和：因为各个桶之间没有进行排重，所以总数的要另外计算
        elasticAggsPojo.setTotal(String.valueOf(total));
        return elasticAggsPojo;
    }

    /**
     * 把桶内金额转化为人民币
     * @param currencyRateMap 币种id--币种汇率
     * @param object 桶对象
     * @param aggs      聚合key，如“aggs01”、“aggs02”
     * @return double
     * @author zcp
     * @date 2019/3/20 10:13
     * @since v1.0
     * @version v1.0
     */
    public double getBucketsAmount(Map<Long, Double> currencyRateMap, Object object, String aggs) {
        JSONObject jsonObject = (JSONObject) object;
        //桶内各个币种
        Long currency = jsonObject.getLong("key");
        //该币种对应的金额
        Double value = jsonObject.getJSONObject(aggs).getDoubleValue("value");

        //按照汇率转为人民币
        return BasicHelper.getAmountByExchangeRate(value, currency, currencyRateMap);
    }

    /**
     * 移除查询条件
     * @param boolQueryBuilder 查询条件
     * @param field 要移除的条件字段
     * @author zcp
     * @date 2019/2/19 22:00
     * @since v1.0
     * @version v1.0
     */
    public void removeSearchCondition(BoolQueryBuilder boolQueryBuilder, String field) {
        List<QueryBuilder> list = boolQueryBuilder.filter();
        Iterator queryIterator = list.iterator();
        while (queryIterator.hasNext()) {
            QueryBuilder queryBuilder = (QueryBuilder) queryIterator.next();
            String key = queryBuilder.getName();
            //转为JSONObject
            JSONObject jsonObject = JSONObject.parseObject(queryBuilder.toString());
            if (jsonObject == null) {
                continue;
            }
            JSONObject filter = jsonObject.getJSONObject(key);
            if (filter == null) {
                continue;
            }
            //如果该条件存在，则移除
            Object object = filter.get(field);
            if (object != null) {
                queryIterator.remove();
                break;
            }
        }
    }
    /**
     * 筛选团队
     * @param teamIndexTypeEnum 团队索引枚举
     * @param corpid 公司id
     * @param isMain 是否是主负责人：0否1是
     * @param userIdIn 员工id数组
     * @return org.elasticsearch.join.query.HasChildQueryBuilder
     * @author zcp
     * @date 2019/3/21 14:38
     * indexTypeEnum 核对完成 已更改为indexModel中方法 @date 2022/5/6 15:35
     * @since v1.0
     * @version v1.0
     */
    public HasChildQueryBuilder teamQueryBuilder(IndexTypeEnum teamIndexTypeEnum, String corpid, Integer isMain, List<String> userIdIn){
        BoolQueryBuilder teamQueryBuilder = boolQuery();
        teamQueryBuilder.filter(termQuery("corpid.keyword", corpid));
        if (isMain != null) {
            teamQueryBuilder.filter(termQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(teamIndexTypeEnum, "isMain"), isMain));
        }
        teamQueryBuilder.filter(termQuery("del",0));
        teamQueryBuilder.filter(termsQuery(SubFormTransferEnum.getEsAttrByIndexTypeAndDbAttr(teamIndexTypeEnum, "userId"), userIdIn));

        HasChildQueryBuilder  hasChildQueryBuilder = JoinQueryBuilders.hasChildQuery(teamIndexTypeEnum.getType(),teamQueryBuilder, ScoreMode.Max);
        return hasChildQueryBuilder;
    }

    /**
     * 判断attr对应的子表单是否为特殊（非单独表）子表单
     * @param attr
     * @param businessType 业务类型，用于获取对应的子表单indexTypeEnum
     * @return
     * indexTypeEnum 核对完成
     */
    public static boolean isSpecialSubForm(String attr, Integer businessType){
        if (Objects.isNull(businessType) ||
                Objects.equals(businessType, XbbRefTypeEnum.SYSTEM.getCode()) ||
                Objects.equals(businessType, XbbRefTypeEnum.PAAS.getCode())) {
            businessType = 1;
        }
        IndexTypeEnum indexTypeEnum = IndexTypeEnum.getByCode(businessType);
        IndexTypeEnum subFormIndexTypeEnum = IndexTypeEnum.getSubFormByParent(indexTypeEnum);
        return isSpecialSubForm(attr, subFormIndexTypeEnum);
    }

    /**
     * 判断attr对应的子表单是否为特殊（非单独表）子表单
     * @param attr
     * @param indexTypeEnum 子表单的indexTypeEnum
     * @return
     * indexTypeEnum 核对完成
     */
    public static boolean isSpecialSubForm(String attr, IndexTypeEnum indexTypeEnum){
        boolean isSpecial = false;
        switch (indexTypeEnum) {
            case IDX_SUB_FORM_DATA:
            case IDX_SAAS_CONTRACT_SUB:
            case IDX_SAAS_QUOTATION_SUB:
            case IDX_SAAS_INSTOCK_SUB:
            case IDX_SAAS_ASSEMBLE_SUB:
            case IDX_SAAS_PAYMENT_SUB:
            case IDX_SAAS_PAYMENT_SHEET_SUB:
            case IDX_SAAS_PAYPLAN_SHEET_SUB:
            case IDX_SAAS_PRODUCTION_ORDER_SUB:
            case IDX_SAAS_PAY_PLAN_SUB:
            case IDX_SAAS_OPPORTUNITY_SUB:
            case IDX_SAAS_BOM_SUB:
            case IDX_SAAS_CUSTOMER_COMMUNICATE_SUB:
            case IDX_SAAS_SUPPLIER_COMMUNICATE_SUB:
            case IDX_SAAS_PURCHASE_SUB:
            case IDX_SAAS_RETURNED_PURCHASE_SUB:
            case IDX_SAAS_INVENTORY_SUB:
            case IDX_SAAS_TRANSFER_SUB:
            case IDX_SAAS_WAREHOUSE_SUB:
            case IDX_SAAS_REFUND_SUB:
            case IDX_SAAS_MARKET_ACTIVITY_SUB:
                break;
            case IDX_SAAS_OUTSTOCK_SUB:
                // TODO 卡特说，先保留在主数据里，至于咋办，不要问我 edit by 吴峰
                if (Objects.equals(ContractOutstockEnum.CUSTOMER_PHONE.getAttr(), attr)) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_CUSTOMER_SUB:
            case IDX_SAAS_CUSTOMER_SUB_SPECIAL:
                if (attr.equals(CustomerManagementEnum.PHONE.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_CONTACT_SUB:
                if (attr.equals(ContactEnum.PHONE.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_INVOICE_SUB:
                if (attr.equals(InvoiceEnum.CUSTOMER_INVOICE.getAttr()) || attr.equals(InvoiceEnum.CUSTOMER_EXPRESS.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_PURCHASE_INVOICE_SUB:
                if (attr.equals(PurchaseInvoiceEnum.CUSTOMER_INVOICE.getAttr()) || attr.equals(PurchaseInvoiceEnum.CUSTOMER_EXPRESS.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_COMMUNICATE_PLAN_SUB:
                if (attr.equals(CommunicatePlanEnum.CONTACT_WAY.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_WORK_REPORT_SUB:
                if (attr.equals(WorkReportDailyEnum.THIS_PLAN.getAttr()) || attr.equals(WorkReportDailyEnum.NEXT_PLAN.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_SUPPLIER_CONTACT_SUB:
                if (attr.equals(SupplierContactEnum.PHONE.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_SUPPLIER_SUB:
                if (attr.equals(SupplierEnum.PHONE.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_PRODUCT_SUB:
                if (attr.equals(ProductEnum.STOCK_NOTIFY.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_WORK_ORDER_SUB:
                if (attr.equals(WorkOrderEnum.CONTACT_INFORMATION.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_CLUE_SUB:
                if (attr.equals(ClueEnum.PHONE.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_SAAS_PAYMENT_TASK_SUB:
                if (attr.equals(PaymentTaskEnum.CONTACT_WAY.getAttr())) {
                    isSpecial = true;
                }
                break;
            case IDX_WORK_ORDER_V2_SUB:
                //因为没有新工单枚举，拿老的用一下
                if (attr.equals(WorkOrderEnum.CONTACT_INFORMATION.getAttr())) {
                    isSpecial = true;
                }
                break;
            case UNKNOWN:
                //没有单独的子表单时，返回true，查询和保存就不会做多余的错误逻辑
                isSpecial = true;
                break;
            default:
                break;
        }
        return isSpecial;
    }

    /**
     * 封装时间聚合条件，并设置格式
     * @param startTime 筛选的开始时间
     * @param endTime 筛选的结束时间
     * @param fieldTime 时间聚合字段
     * @param timeType 筛选类型
     * @return org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder
     * @author zcp
     * @date 2019/2/20 20:02
     * @since v1.0
     * @version v1.0
     */
    public ValuesSourceAggregationBuilder getDateHistogramAggregation(Integer startTime, Integer endTime, String fieldTime, int timeType) {
        AggMorePojo aggMorePojo = new AggMorePojo(fieldTime);
        return getDateHistogramAggregation(startTime, endTime, timeType, aggMorePojo);
    }
    /**
     * 封装时间聚合条件，并设置格式
     * @param startTime 筛选的开始时间
     * @param endTime 筛选的结束时间
     * @param timeType 筛选类型
     * @param aggMorePojo 时间聚合分桶名及聚合字段名pojo
     * @return org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public ValuesSourceAggregationBuilder getDateHistogramAggregation(Integer startTime, Integer endTime, int timeType, AggMorePojo aggMorePojo) {
        String aggs = aggMorePojo.getAggs();
        String aggsFieldTime = aggMorePojo.getAggsFieldTime();
        /*--------处理聚合条件--------*/
        ValuesSourceAggregationBuilder dateHistogramAggregation = AggregationBuilders.dateHistogram(aggs).field(aggsFieldTime);
        //时区，格式
        dateHistogramAggregation.timeZone(ZoneOffset.systemDefault());
        dateHistogramAggregation.format("yyyy-MM-dd");
        //设置extendedBounds
        ((DateHistogramAggregationBuilder) dateHistogramAggregation).extendedBounds(getExtendedBounds(startTime, endTime));
        //设置dateHistogramInterval
        DateHistogramInterval dateHistogramInterval = setDateHistogramInterval(timeType);
        ((DateHistogramAggregationBuilder) dateHistogramAggregation).calendarInterval(dateHistogramInterval);
        return dateHistogramAggregation;
    }

    /**
     * 封装时间聚合条件，并设置格式
     * @param startTime 筛选的开始时间
     * @param endTime 筛选的结束时间
     * @param timeType 筛选类型
     * @param aggMorePojo 时间聚合分桶名及聚合字段名pojo
     * @return org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public ValuesSourceAggregationBuilder getDateHistogram4TermAndSum(Integer startTime, Integer endTime, int timeType, AggMorePojo aggMorePojo) {
        //第一层时间分桶
        ValuesSourceAggregationBuilder valuesSourceAggregationBuilder = getDateHistogramAggregation(startTime, endTime, timeType, aggMorePojo);
        //第二层字段聚合
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms(aggMorePojo.getAggs2()).field(aggMorePojo.getAggsTermsField());
        //第三层sum
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggMorePojo.getAggs3()).field(aggMorePojo.getAggsSumField());
        termsAggregationBuilder.subAggregation(sumAggregationBuilder);
        valuesSourceAggregationBuilder.subAggregation(termsAggregationBuilder);
        return valuesSourceAggregationBuilder;
    }

    /**
     * 双层聚合：上一层terms，下一层sum
     * 默认不需要排序，且terms分桶size为最大
     * @param termField 要聚合的字段
     * @param sumField  求和的字段
     * @param aggs      聚合key，如“aggs01”、“aggs02”
     * @param aggsSub   聚合key，如“aggs02”、“aggs03”，与aggsUp对应，是aggsUp的下一级聚合
     * @return org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder
     * @author zcp
     * @date 2019/2/27 14:35
     * @version v1.0
     * @since v1.0
     * @update 2020/12/7 by zcp 标记为Deprecated，后续各统计业务最好不要调用该方法，因为terms聚合尽量不要使用Integer.MAX_VALUE。建议使用方法：getTermsAggregation4TwoNoOrder
     */
    @Deprecated
    public TermsAggregationBuilder getTermsAggregation4Two(String termField, String sumField, String aggs, String aggsSub) {
        //默认不需要排序，且terms分桶size为最大
        return getTermsAggregation4TwoNoOrder(Integer.MAX_VALUE, termField, sumField, aggs, aggsSub);
    }

    /**
     * 双层聚合：上一层terms，下一层sum
     * 默认不需要排序，且terms分桶size为最大
     *
     * @param termSize  分桶大小
     * @param termField 要聚合的字段
     * @param sumField  求和的字段
     * @param aggs      聚合key，如“aggs01”、“aggs02”
     * @param aggsSub   聚合key，如“aggs02”、“aggs03”，与aggsUp对应，是aggsUp的下一级聚合
     * @return org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder
     * @author zcp
     * @version v1.0
     * @since v1.0
     */
    public TermsAggregationBuilder getTermsAggregation4TwoNoOrder(int termSize, String termField, String sumField, String aggs, String aggsSub) {
        //默认不需要排序，且terms分桶size为最大
        return getTermsAggregationOrder4Two(termSize, null, termField, sumField, aggs, aggsSub);
    }

    /**
     * 双层聚合：上一层terms，下一层sum
     * 根据termSize控制分桶大小，根据order控制排序
     *
     * @param termSize  第一层terms聚合桶的size大小
     * @param order     指定排序，如按照aggsSub分层的sumField求和结果降序，则order传入：Terms.Order.aggregation(aggsSub, false);
     * @param termField 要聚合的字段
     * @param sumField  求和的字段
     * @param aggs      聚合key，如“aggs01”、“aggs02”
     * @param aggsSub   聚合key，如“aggs02”、“aggs03”，与aggsUp对应，是aggsUp的下一级聚合
     * @return org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder
     * @throws
     * @author zcp
     * @date 2019/4/5 11:25
     * @version v1.0
     * @since v1.0
     */
    public TermsAggregationBuilder getTermsAggregationOrder4Two(int termSize, BucketOrder order, String termField, String sumField, String aggs, String aggsSub) {
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms(aggs).field(termField);
        termsAggregationBuilder.size(termSize);
        termsAggregationBuilder.executionHint(ElasticConstant.EXECUTION_HINT);
        if (order != null) {
            //指定排序，如按照aggsSub分层的sumField求和结果降序，则order传入：Terms.Order.aggregation(aggsSub, false);
            termsAggregationBuilder.order(order);
        }
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggsSub).field(sumField);
        termsAggregationBuilder.subAggregation(sumAggregationBuilder);
        return termsAggregationBuilder;
    }
    /**
     * 双层聚合：上一层terms，下一层两个sum
     * 默认不需要排序，且terms分桶size为最大
     *
     * @param termField 要聚合的字段
     * @param sumField  求和的字段
     * @param sumField2 第二个求和字段
     * @return org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder
     * @author zcp
     * @date 2019/4/5 14:27
     * @since v1.0
     * @version v1.0
     */
    public TermsAggregationBuilder getTermsAggregationOrder4TwoSum(String termField, String sumField, String sumField2){
        //第一层聚合
        String aggs = "aggs01";
        String aggsSub = "aggs021";
        String aggsSub2 = "aggs022";
        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms(aggs).field(termField);
        termsAggregationBuilder.size(Integer.MAX_VALUE);
        termsAggregationBuilder.executionHint(ElasticConstant.EXECUTION_HINT);
        BucketOrder order = BucketOrder.aggregation(aggsSub, false);
        termsAggregationBuilder.order(order);
        AggregatorFactories.Builder subFactories = new AggregatorFactories.Builder();
        //2.1层聚合
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggsSub).field(sumField);
        subFactories.addAggregator(sumAggregationBuilder);
        //2.2层聚合
        SumAggregationBuilder sumAggregationBuilder2 = AggregationBuilders.sum(aggsSub2).field(sumField2);
        subFactories.addAggregator(sumAggregationBuilder2);
        termsAggregationBuilder.subAggregations(subFactories);
        return termsAggregationBuilder;
    }

    /**
     * 三层聚合：上一层terms，下一层两个sum + 一个script
     * 默认不需要排序，且terms分桶size为最大
     *
     * @param termField 要聚合的字段
     * @param sumField  求和的字段
     * @param sumField2 第二个求和字段
     * @param script 第三个脚本字段（如两字段相乘）
     * @return com.xbongbong.paas.elasticsearch.pojo.AggSubFactoriesPojo
     * @author zcp
     * @since v1.0
     * @version v1.0
     */
    public AggSubFactoriesPojo getTermsAggregation4ThreeSumAndMcl(int termSize, String termField, String sumField, String sumField2, Script script){
        AggSubFactoriesPojo aggSubFactoriesPojo = new AggSubFactoriesPojo();
        String aggsSub1 = aggSubFactoriesPojo.getAggsSub1();
        String aggsSub2 = aggSubFactoriesPojo.getAggsSub2();
        String aggsSub3 = aggSubFactoriesPojo.getAggsSub3();
        TermsAggregationBuilder termsAggregationBuilder = aggSubFactoriesPojo.getTermsAggregationBuilder().field(termField);
        termSize = termSize == 0 ? 1 : termSize;
        termsAggregationBuilder.size(termSize);
        termsAggregationBuilder.executionHint(ElasticConstant.EXECUTION_HINT);
        BucketOrder order = BucketOrder.aggregation(aggsSub1, false);
        termsAggregationBuilder.order(order);
        AggregatorFactories.Builder subFactories = new AggregatorFactories.Builder();
        //2.1层聚合
        SumAggregationBuilder sumAggregationBuilder = AggregationBuilders.sum(aggsSub1).field(sumField);
        subFactories.addAggregator(sumAggregationBuilder);
        //2.2层聚合
        SumAggregationBuilder sumAggregationBuilder2 = AggregationBuilders.sum(aggsSub2).field(sumField2);
        subFactories.addAggregator(sumAggregationBuilder2);
        //2.3层聚合
        SumAggregationBuilder sumAggregationBuilder3 = AggregationBuilders.sum(aggsSub3).script(script);
        subFactories.addAggregator(sumAggregationBuilder3);
        termsAggregationBuilder.subAggregations(subFactories);
        return aggSubFactoriesPojo;
    }

    /**
     * 两个平均值参数聚合
     *
     * @param sumFieldOne 第一个平均值的聚合参数
     * @param sumFieldTwo 第二个平均值的聚合参数
     * @return java.util.List<org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder>
     * @author 徐俊杰
     * @date 2019/4/17 14:23
     * @since v1.0
     */
    public List<ValuesSourceAggregationBuilder> twoAverageAggParam(String sumFieldOne, String sumFieldTwo) {
        AvgAggregationBuilder avgAggOne = AggregationBuilders.avg("aggs011").field(sumFieldOne);
        AvgAggregationBuilder avgAggTwo = AggregationBuilders.avg("aggs012").field(sumFieldTwo);
        return Arrays.asList(avgAggOne, avgAggTwo);
    }

    /**
     * 封装两个平均值的聚合结果
     *
     * @param indexTypeEnum 索引，用于获取index和type
     * @param boolQueryBuilder 查询条件
     * @param aggs 聚合条件
     * @return com.alibaba.fastjson.JSONObject
     * @author 徐俊杰
     * @date 2019/4/17 14:26
     * @since v1.0
     */
    public JSONObject twoAverage(IndexTypeEnum indexTypeEnum, BoolQueryBuilder boolQueryBuilder, List<ValuesSourceAggregationBuilder> aggs) throws XbbException {
        JSONObject jsonObject = multElasticAggs4NoBuckets2Object(indexTypeEnum, boolQueryBuilder, aggs);
        JSONObject aggregations = jsonObject.getJSONObject("aggregations");
        double averageOne = 0D;
        double averageTwo = 0D;
        JSONObject aggs011 = aggregations.getJSONObject("aggs011");
        averageOne = aggs011.getDoubleValue("value");
        JSONObject aggs012 = aggregations.getJSONObject("aggs012");
        averageTwo = aggs012.getDoubleValue("value");
        JSONObject resultJsonObject = new JSONObject();
        resultJsonObject.put("averageOne", averageOne);
        resultJsonObject.put("averageTwo", averageTwo);
        return resultJsonObject;
    }

    /**
     * 根据游标查询数据
     * @param scrollId 游标id
     * @return com.xbongbong.paas.elasticsearch.pojo.ElasticFindPojo 查询结果相应信息塞入ElasticFindPojo
     * @throws IOException
     * @author zcp
     * @date 2019/2/21 22:35
     * @since v1.0
     * @version v1.0
     */
    /*private ElasticFindPojo elasticSearchListScroll(String scrollId) throws IOException {
        Client client = elasticsearchTemplate.getClient();
        SearchScrollRequestBuilder search = client.prepareSearchScroll(scrollId);
        search.setScroll("1m");
        SearchResponse searchResponse = search.execute().actionGet();
        return formatReturn(searchResponse);
    }*/

    /**
     * 格式化查询结果
     * @param searchResponse 查询返回结果
     * @return com.xbongbong.paas.elasticsearch.pojo.ElasticFindPojo 查询结果相应信息塞入ElasticFindPojo
     * @author zcp
     * @date 2019/2/21 22:13
     * @since v1.0
     * @version v1.0
     */
    private ElasticFindPojo formatReturn(SearchResponse searchResponse){
        SearchHits searchHits = searchResponse.getHits();
        //把id置入格式化后的hits
        SearchHit[] searchHitsArr = searchHits.getHits();
        List<JSONObject> formatHits = new ArrayList<>();
        for (SearchHit searchHitFields : searchHitsArr){
            JSONObject sourceObject = JSONObject.parseObject(searchHitFields.getSourceAsString());
            formatHits.add(sourceObject);
        }
        ElasticFindPojo elasticFindPojo = new ElasticFindPojo();
        elasticFindPojo.setTotal(searchHits.getTotalHits().value);
        elasticFindPojo.setScrollId(searchResponse.getScrollId());
        elasticFindPojo.setHits(formatHits);
        return elasticFindPojo;
    }
    /**
     * 设置时间段是按照月or日间隔
     * 例如：筛选全年，要转化为12个月；筛选某一月，要转化为该月内所有日
     * @param timeType 时间筛选类型，对应TimeTypeEnum枚举
     * @return java.lang.String interval
     * @author zcp
     * @date 2019/2/13 10:52
     * @since v1.0
     * @version v1.0
     */
    private DateHistogramInterval setDateHistogramInterval(Integer timeType) {
        DateHistogramInterval dateHistogramInterval = null;
        if (timeType.equals(TimeTypeEnum.YEAR.getCode())
                || timeType.equals(TimeTypeEnum.YEAR_HALF_UP.getCode())
                || timeType.equals(TimeTypeEnum.YEAR_HALF_DOWN.getCode())
                || timeType.equals(TimeTypeEnum.SEASON.getCode())) {
            dateHistogramInterval = DateHistogramInterval.MONTH;
        } else if (timeType.equals(TimeTypeEnum.MONTH.getCode())
                || timeType.equals(TimeTypeEnum.MONTH_LAST.getCode())
                || timeType.equals(TimeTypeEnum.WEEK.getCode())
                || timeType.equals(TimeTypeEnum.WEEK_LAST.getCode())
                || timeType.equals(TimeTypeEnum.DAY.getCode())
                || timeType.equals(TimeTypeEnum.DAY_LAST.getCode())
                || timeType.equals(TimeTypeEnum.DYNAMIC.getCode())
                || timeType.equals(TimeTypeEnum.CUSTOM.getCode())) {
            dateHistogramInterval = DateHistogramInterval.DAY;
        }
        return dateHistogramInterval;
    }

    private static DateHistogramInterval setBiDateHistogramInterval(String format) {
        DateHistogramInterval dateHistogramInterval;
        IntervalTypeEnum intervalTypeEnum = IntervalTypeEnum.getByFormatStr(format);
        switch (intervalTypeEnum) {
            case YEAR:
                dateHistogramInterval = DateHistogramInterval.YEAR;
                break;
            case YEAR_WEEK:
                dateHistogramInterval = DateHistogramInterval.WEEK;
                break;
            case YEAR_MONTH:
                dateHistogramInterval = DateHistogramInterval.MONTH;
                break;
            case YEAR_QUARTER:
                dateHistogramInterval = DateHistogramInterval.QUARTER;
                break;
            case YEAR_MONTH_DAY:
            default:
                dateHistogramInterval = DateHistogramInterval.DAY;
                break;
        }
        return dateHistogramInterval;
    }

    /**
     * extended_bounds参数会强制返回一整段时间内的数据
     * @param startTime 筛选的开始时间
     * @param endTime 筛选的结束时间
     * @return org.elasticsearch.search.aggregations.bucket.histogram.ExtendedBounds
     * @author zcp
     * @date 2019/2/13 11:13
     * @since v1.0
     * @version v1.0
     */
    private ExtendedBounds getExtendedBounds(Integer startTime, Integer endTime) {
        SimpleDateFormat sdfDate = new SimpleDateFormat("yyyy-MM-dd");
        String min = DateUtil.getString(startTime, sdfDate);
        //比如2017全年应该是2017-01-01到2017-12-31，如果是2018-01-01会返回13个桶
        String max = DateUtil.getString(endTime-1, sdfDate);
        ExtendedBounds extendedBounds = new ExtendedBounds(min, max);
        return extendedBounds;
    }

    /**
     * es查询或聚合时输出关键信息，方便查询问题
     * @param indexTypeEnum 索引枚举
     * @param prefix 输出前缀，可为空
     * @return java.lang.StringBuilder
     * @author zcp
     * @date 2019/5/17 19:58
     * @since v1.0
     * @version v1.0
     */
    private StringBuilder setStringBuilder4Log(IndexTypeEnum indexTypeEnum, String prefix) {
        StringBuilder syso = new StringBuilder();
        syso.append(prefix);
        syso.append("indexTypeEnum:");
        syso.append(indexTypeEnum);
        syso.append(" index:");
        syso.append(indexTypeEnum.getIndex());
        syso.append(" type:");
        syso.append(indexTypeEnum.getType());
        return syso;
    }

    public ValuesSourceAggregationBuilder getBiDateHistogramAggregation(String aggName, String fieldTime, String dateGroupType, Boolean asc, boolean yearChainFlag) {
        /*--------处理聚合条件--------*/
        ValuesSourceAggregationBuilder dateHistogramAggregation = AggregationBuilders.dateHistogram(aggName).field(fieldTime);
        dateHistogramAggregation.format("yyyy-MM-dd");
        //设置dateHistogramInterval
        DateHistogramInterval dateHistogramInterval = setBiDateHistogramInterval(dateGroupType);
        dateHistogramAggregation.timeZone(ZoneOffset.systemDefault());
        ((DateHistogramAggregationBuilder) dateHistogramAggregation).calendarInterval(dateHistogramInterval);
        // 同环比要设置为0或不加
        if (!yearChainFlag) {
            ((DateHistogramAggregationBuilder) dateHistogramAggregation).minDocCount(1);
        }
        if (asc != null) {
            ((DateHistogramAggregationBuilder) dateHistogramAggregation).order(BucketOrder.aggregation("_key", asc));
        }
        return dateHistogramAggregation;
    }



    /**
     * 判断该表单是否走团队逻辑
     * @param businessType 表单类型
     * @return true 走团队逻辑
     * @author zhouwq
     * @date 2019/6/25 15:59
     */
    public static boolean isTeamLogicByBusinessType(Integer businessType) {
        //判断是否走团队逻辑
        boolean isTeam = false;
        //判断不为pass表
        if (businessType != null && businessType != XbbRefTypeEnum.UNKNOWN.getCode()) {
            if (Objects.equals(XbbRefTypeEnum.COMMUNICATE_PLAN.getCode(), businessType)) {
                return false;
            }
            // 判断该表单是否走团队逻辑
            isTeam = UserTeamEnum.hasTeam(businessType);
        }
        return isTeam;
    }

    /**
     * 判断该字段类型是否走团队逻辑
     * @param fieldType 字段类型
     * @param businessType 业务code
     * @return true or false
     */
    public static boolean isTeamLogicByFieldType(Integer fieldType, Integer businessType) {
        boolean isTeamForm = isTeamLogicByBusinessType(businessType);
        boolean isTeamFieldType = FieldTypeEnum.OWNERID.getType().equals(fieldType) || FieldTypeEnum.COUSERID.getType().equals(fieldType);
        return isTeamForm && isTeamFieldType;
    }


    /**
     * 判断是否为时间类型
     * @param fieldType 字段类型
     * @author zhouwq
     * @date 2019/5/22 10:11
     * @return true为时间类型
     * @update 迁移至FieldTypeEnum
     */
    public static boolean isDateTimeByFieldType(Integer fieldType) {
        return FieldTypeEnum.isDateTimeByFieldType(fieldType);
    }
}
